package com.bandi.tharshesnows.geolocating;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;

import org.apache.http.util.ByteArrayBuffer;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.WindowManager;

import com.bandi.tharshesnows.model.County;
import com.bandi.tharshesnows.model.Location;
import com.bandi.tharshesnows.model.Resort;
import com.bandi.tharshesnows.model.Skirun;
import com.bandi.tharshesnows.pics.PictureStorage;

public class GeocoderService {

  private static final String GEOCODER_PATTERN = "http://maps.google.com/maps/api/geocode/json?address={0}&ka&sensor=false";

  private PictureStorage pictureStorage;

  private static final int MDPI_WIDTH = 300;
  private static final int MDPI_HEIGHT = 120;
  private int width, height;
  private float density;

  private static GeocoderService INSTANCE;

  private GeocoderService(Context context) {
    DisplayMetrics metrics = new DisplayMetrics();
    WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    manager.getDefaultDisplay().getMetrics(metrics);
    density = metrics.density;
    // width = (int) (MDPI_WIDTH * metrics.density);
    // height = (int) (MDPI_HEIGHT * metrics.density);
    width = 640;
    height = 320;
    pictureStorage = PictureStorage.getInstance();
  }

  public static void init(Context context) {
    if (INSTANCE == null) {
      INSTANCE = new GeocoderService(context);
    }
  }

  public static GeocoderService getInstance() {
    if (INSTANCE == null) {
      throw new IllegalStateException("GeocoderService is not initilaized.");
    }
    return INSTANCE;
  }

  public Location getLocation(County county) {
    String address = county.getName() + ", Romania";
    Log.d("SNOW", "Try to get geolocation for " + address);
    try {
      Location location = getGeolocation(address);
      ArrayList<Location> resortLocations = new ArrayList<Location>();
      for (Resort resort : county.getResorts()) {
        Location resortlocation = getGeolocation(resort.getName() + ", " + county.getName() + ", Romania");
        if (resortlocation != null) {
          resortLocations.add(resortlocation);
        }
      }
      if (resortLocations.isEmpty()) {
        location.setStaticMapImagePath(StaticMaps.getMapPath(county.getName(), 10, width, height, location));
      }
      else {
        Location[] locations = new Location[resortLocations.size()];
        resortLocations.toArray(locations);
        location.setStaticMapImagePath(StaticMaps.getMapPath(county.getName(), 10, width, height, location, locations));
      }
      return location;
    }
    catch (Exception e) {
      Log.e("SNOW", "Failed to get geolocation for " + address, e);
      return null;
    }
  }

  public Location getLocation(Skirun skirun) {
    int imgHeight = (int) (150 * density);
    String address = skirun.getName() + ", " + skirun.getResort().getName() + ", "
        + skirun.getResort().getCounty().getName() + ", Romania";
    Log.d("SNOW", "Try to get geolocation for " + address);
    try {
      Location l = getGeolocation(address);
      if (l == null) {
        String address2 = skirun.getResort().getName() + ", " + skirun.getResort().getCounty().getName() + ", Romania";
        Log.d("SNOW", "Failed to get geolocation for " + address + ", try: " + address2);
        l = getGeolocation(address2);
        if (l == null) {
          Log.e("SNOW", "Failed to get geolocation for " + address2);
          return null;
        }
        l.setStaticMapImagePath(StaticMaps.getMapPath(skirun.getName(), 10, width, imgHeight, l));
        return l;
      }
      l.setStaticMapImagePath(StaticMaps.getMapPath(skirun.getName(), 10, width, imgHeight, l));
      return l;

    }
    catch (Exception e) {
      Log.e("SNOW", "Failed to get geolocation for " + address, e);
      return null;
    }
  }

  private Location getGeolocation(String address) throws IOException, JSONException {
    BufferedInputStream bis = null;
    try {
      URL url = new URL(MessageFormat.format(GEOCODER_PATTERN, URLEncoder.encode(address, "UTF-8")));
      URLConnection conn = url.openConnection();
      InputStream is = conn.getInputStream();
      bis = new BufferedInputStream(is);
      ByteArrayBuffer baf = new ByteArrayBuffer(5000);
      byte buffer[] = new byte[2048];
      while ((bis.read(buffer)) != -1) {
        baf.append(buffer, 0, buffer.length);
      }
      /* Convert the Bytes read to a String. */
      String jsonStr = new String(baf.toByteArray());
      JSONObject json = new JSONObject(jsonStr);
      JSONArray array = json.getJSONArray("results");
      json = array.getJSONObject(0);
      json = json.getJSONObject("geometry");
      json = json.getJSONObject("location");
      Log.d("SNOW", "geo: " + json);
      return new Location(json.getDouble("lat"), json.getDouble("lng"));
    }
    catch (Throwable t) {
      return null;
    }
    finally {
      if (bis != null) {
        bis.close();
      }
    }
  }

}
